package dryven.init;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;

import dryven.config.ApplicationConfiguration;
import dryven.discovery.ClassDiscovery;
import dryven.discovery.ClassDiscoveryListener;
import dryven.model.di.DependencyService;
import dryven.request.CompositeRequestListener;
import dryven.request.FrontController;
import dryven.request.RequestFrontController;
import dryven.request.RequestLifeCycleListener;
import dryven.request.controller.ControllerFactory;
import dryven.request.controller.paramtransform.ActionParameterBindCandidate;
import dryven.request.controller.paramtransform.ActionParameterFillCandidate;
import dryven.request.controller.paramtransform.ActionParameterTransformCandidate;
import dryven.request.controller.paramtransform.ActionParameterTransformer;
import dryven.request.controller.paramtransform.CompositeActionParameterTransformer;
import dryven.request.routing.DefaultRoutingService;
import dryven.request.routing.Router;
import dryven.request.routing.RoutingService;

public abstract class AbstractFrontControllerFactory implements FrontControllerFactory {

	private ClassDiscovery classDiscovery;
	private ApplicationConfiguration applicationConfiguration;
	private RoutingService _routingService;
	private CompositeActionParameterTransformer _transformer;
	
	public FrontController createFrontController(ApplicationConfiguration appCfg) {
		applicationConfiguration = appCfg;
		classDiscovery = new ClassDiscovery(applicationConfiguration.getModelPackages(),applicationConfiguration.getClassPathEntries());
		
		FrontController fc = create();
		
		classDiscovery.scan();	//won't do anything if nobody added any listeners
		return fc;
	}
	
	protected FrontController create() {
		RoutingService rs = getRoutingService();
		
		return new RequestFrontController(rs, getDependencyService(),createRequestLifeCycleListener());
	}
	
	protected RequestLifeCycleListener createRequestLifeCycleListener() {
		List<RequestLifeCycleListener> listeners = new LinkedList<RequestLifeCycleListener>();
		addRequestLifeCycleListeners(listeners);
		return new CompositeRequestListener(listeners);
	}
	
	protected void addRequestLifeCycleListeners(Collection<RequestLifeCycleListener> listeners) {
		
	} 
	
	protected ApplicationConfiguration getApplicationConfiguration() {
		return applicationConfiguration;
	}
	
	protected void addDiscoveryListener(ClassDiscoveryListener l) {
		classDiscovery.addListener(l);
	}
	
	protected RoutingService getRoutingService() {
		if(_routingService==null) {
			_routingService = createRoutingService();
		}
		return _routingService;
	}
	
	protected RoutingService createRoutingService() {
		Collection<Router> routers = new LinkedList<Router>();
		addRouters(routers);
		ActionParameterTransformer transformer = getActionParameterTransformer();
		RoutingService s = new DefaultRoutingService(routers, transformer,getApplicationConfiguration().getApplicationRootURLPath());
		return s;
	}
	
	protected abstract void addRouters(Collection<Router> routers);
	
	protected ActionParameterTransformer getActionParameterTransformer() {
		if(_transformer==null) {
			//construct object first to avoid circular dependencies (e.g. with FormDecoder)
			_transformer = new CompositeActionParameterTransformer();
			_transformer.setTransformers(
					createActionParameterTransformers(), 
					createActionParameterBinders(), 
					createActionParameterFillers()
				);
		}
		return _transformer;
	}
	
	protected Iterable<ActionParameterTransformCandidate> createActionParameterTransformers() {
		Collection<ActionParameterTransformCandidate> transformers = new LinkedList<ActionParameterTransformCandidate>();
		addActionParameterTransformers(transformers);
		return transformers;
	}
	
	protected Iterable<ActionParameterBindCandidate> createActionParameterBinders() {
		Collection<ActionParameterBindCandidate> binders = new LinkedList<ActionParameterBindCandidate>();
		addActionParameterBinders(binders);
		return binders;
	}

	protected Iterable<ActionParameterFillCandidate> createActionParameterFillers() {
		Collection<ActionParameterFillCandidate> fillers = new LinkedList<ActionParameterFillCandidate>();
		addActionParameterFillers(fillers);
		return fillers;
	}
	
	protected abstract void addActionParameterTransformers(Collection<ActionParameterTransformCandidate> transformers);
	protected abstract void addActionParameterBinders(Collection<ActionParameterBindCandidate> binders);
	protected abstract void addActionParameterFillers(Collection<ActionParameterFillCandidate> fillers);
	protected abstract ControllerFactory createControllerFactory();

	protected abstract DependencyService getDependencyService();
}